#ifndef _DNS_H_
#define _DNS_H_

#include <crt_util.h>

#ifdef __cplusplus
extern "C" {
#endif

#define BigtoLittle16(A) ((((uint16)(A)&0xff00) >> 8) | (((uint16)(A)&0x00ff) << 8))
#define BigtoLittle32(A) \
    ((((uint32)(A)&0xff000000) >> 24) | (((uint32)(A)&0x00ff0000) >> 8) | (((uint32)(A)&0x0000ff00) << 8) | (((uint32)(A)&0x000000ff) << 24))

#define DNS_PORT 53

#define DNS_QUERY    0
#define DNS_RESPONSE 1

#define DNS_TYPE_A     1 // ipv4
#define DNS_TYPE_NS    2
#define DNS_TYPE_CNAME 5
#define DNS_TYPE_SOA   6
#define DNS_TYPE_WKS   11
#define DNS_TYPE_PTR   12
#define DNS_TYPE_HINFO 13
#define DNS_TYPE_MX    15
#define DNS_TYPE_AAAA  28 // ipv6
#define DNS_TYPE_AXFR  252
#define DNS_TYPE_ANY   255

#define DNS_CLASS_IN 1

#define DNS_NAME_MAXLEN 256

// sizeof(dnshdr_t) = 12
typedef struct dnshdr_s {
    uint16_t transaction_id;
    // flags
#if BYTE_ORDER == LITTLE_ENDIAN
    uint8_t rd : 1;
    uint8_t tc : 1;
    uint8_t aa : 1;
    uint8_t opcode : 4;
    uint8_t qr : 1;

    uint8_t rcode : 4;
    uint8_t cd : 1;
    uint8_t ad : 1;
    uint8_t res : 1;
    uint8_t ra : 1;
#elif BYTE_ORDER == BIG_ENDIAN
    uint8_t qr : 1; // DNS_QUERY or DNS_RESPONSE
    uint8_t opcode : 4;
    uint8_t aa : 1; // authoritative
    uint8_t tc : 1; // truncated
    uint8_t rd : 1; // recursion desired

    uint8_t ra : 1;  // recursion available
    uint8_t res : 1; // reserved
    uint8_t ad : 1;  // authenticated data
    uint8_t cd : 1;  // checking disable
    uint8_t rcode : 4;
#else
#error "BYTE_ORDER undefined!"
#endif
    uint16_t nquestion;
    uint16_t nanswer;
    uint16_t nauthority;
    uint16_t naddtional;
} dnshdr_t;

typedef struct dns_rr_s {
    char name[DNS_NAME_MAXLEN]; // original domain, such as www.example.com
    uint16_t rtype;
    uint16_t rclass;
    uint32_t ttl;
    uint16_t datalen;
    char* data;
} dns_rr_t;

typedef struct dns_s {
    dnshdr_t hdr;
    dns_rr_t* questions;
    dns_rr_t* answers;
    dns_rr_t* authorities;
    dns_rr_t* addtionals;
} dns_t;

// www.example.com => 3www7example3com
int dns_name_encode(const char* domain, char* buf);
// 3www7example3com => www.example.com
int dns_name_decode(const char* buf, char* domain);
int dns_rr_pack(dns_rr_t* rr, char* buf, int len);
int dns_rr_unpack(char* buf, int len, dns_rr_t* rr, int is_question);
int dns_pack(dns_t* dns, char* buf, int len);
int dns_unpack(char* buf, int len, dns_t* dns);
// NOTE: free dns->rrs
void dns_free(dns_t* dns);
// dns_pack -> sendto -> recvfrom -> dns_unpack
int dns_query(dns_t* query, dns_t* response, const char* nameserver /* DEFAULT("127.0.1.1")*/);
// domain -> dns_t query; -> dns_query -> dns_t response; -> addrs
int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameserver /*DEFAULT("127.0.1.1")*/);

#ifdef __cplusplus
}
#endif

#endif //_DNS_H_
